﻿#include "JBarChart.h"

#include <QPainter>
#include <QPointF>
#include <QtMath>
#include <QDebug>

JBarChart::JBarChart(QWidget *parent)
    : QWidget{parent}
{
    initDefaultColorList();
    initAnimation();

    _useAnimation = true;
    _useMultiColor = false;
    _textColor = QColor(99, 178, 238);
    _barColor = QColor(99, 178, 238);
    _gridColor = Qt::gray;
}

void JBarChart::initDefaultColorList()
{
    _defaultColorList << QColor(99, 178, 238) << QColor(118, 218, 145) << QColor(248, 203, 127)
                      << QColor(248, 149, 136) << QColor(124, 214, 207) << QColor(145, 146, 171)
                      << QColor(120, 152, 225) << QColor(239, 166, 102) << QColor(237, 221, 134)
                      << QColor(153, 135, 206) << QColor(99, 178, 238) << QColor(118, 218, 145);
}

void JBarChart::initAnimation()
{
    _height = 0;
    _pAnimation = new QPropertyAnimation(this, "_height");
    _pAnimation->setDuration(750);
    _pAnimation->setStartValue(_height);
    // 这里获取的this->height()可能和实际的并不相同，因为调用当前类时resize是在构造函数之后的，有两个解决办法：
    // 1、这里可以尽可能的给一个大的值，除了动画变化较快，不会有什么问题
    // 2、在 resizeEvent 中重写调用 setEndValue，反正只有调用 setValue 时，启动一次动画，不会有多次动画效果
    _pAnimation->setEndValue(this->height());
    _pAnimation->setEasingCurve(QEasingCurve::OutQuad);
}

void JBarChart::drawBar(QPainter *painter)
{
    // 设置画笔
    QPen pen(_gridColor);
    pen.setWidth(2);
    pen.setStyle(Qt::SolidLine);
    painter->setPen(pen);

    // 设置画刷
    QBrush brush(_barColor);
    brush.setStyle(Qt::SolidPattern);
    painter->setBrush(brush);

    // 柱状图的宽度、宽度
    double dbWidth = this->width() - 2 * BAR_OUT_MARGIN;
    double dbHeight = this->height() - 2 * BAR_OUT_MARGIN;

    // 每一等分的长度, 左右各空出一份、每个柱状图占4份，每两个柱状图之间空2份
    int nValueCount = _valueList.count();
    int nDivideCount = 2 + 4 * nValueCount + 2 * (nValueCount - 1);
    double dbDivideLen = dbWidth / nDivideCount;

    // 绘制底边
    int nBottomHeight = this->height() - BAR_OUT_MARGIN;
    painter->drawLine(QPointF(BAR_OUT_MARGIN, nBottomHeight), QPointF(this->width() - BAR_OUT_MARGIN, nBottomHeight));

    // 绘制X轴刻度
    int nXTickCount = nValueCount + 1;
    int nTickHeight = nBottomHeight + TICK_LENGTH;
    // 每个X轴刻度的长度
    double dbXTickLen = dbWidth / (nXTickCount - 1);
    double dbTempX = BAR_OUT_MARGIN;
    for (int i = 0; i < nXTickCount; i++)
    {
        painter->drawLine(QPointF(dbTempX, nBottomHeight), QPointF(dbTempX, nTickHeight));
        dbTempX += dbXTickLen;
    }

    // 绘制Y轴刻度
    // 找出最大值
    double dbMax = -1 * DBL_MIN;
    for (int i = 0; i < nValueCount; i++)
    {
        if (_valueList.at(i) > dbMax)
            dbMax = _valueList.at(i);
    }

    // Y轴默认6个刻度，
    int nYTickCount = 6;
    // 以最大值为基准，计算最大值平均分布在每一个刻度中的值
    //int nYTickValue = dbMax / (nYTickCount - 1);
    int nYTickValue = dbMax / nYTickCount + 1;
    // 每个Y轴刻度的长度
    double dbYTickLen = dbHeight / nYTickCount;

    pen.setColor(_gridColor);
    pen.setWidth(1);
    pen.setStyle(Qt::SolidLine);
    painter->setPen(pen);

    int pointSize = qMin(this->width(), this->height()) / BAR_FONTSIZE_FACTOR;
    painter->setFont(QFont("Arial", pointSize, QFont::Bold));
    QFontMetricsF fm = QFontMetricsF(painter->font());

    double dbTempY = BAR_OUT_MARGIN;
    QString strText;
    int textWidth, textHeight;
    for (int i = 0; i <= nYTickCount; i++)
    {
        if (i < nYTickCount)
            painter->drawLine(QPointF(BAR_OUT_MARGIN, dbTempY), QPointF(this->width() - BAR_OUT_MARGIN, dbTempY));

        // 绘制Y轴刻度
        painter->save();
        pen.setColor(_textColor);
        painter->setPen(pen);

        strText = QString::number(nYTickValue * (nYTickCount - i));
        textWidth = (int)fm.horizontalAdvance(strText);
        textHeight = (int)fm.height();
        painter->drawText(QPointF(BAR_OUT_MARGIN - textWidth - 10,
                                  dbTempY + textHeight / 4),
                          strText);

        painter->restore();

        dbTempY += dbYTickLen;
    }

    painter->setPen(QPen(Qt::NoPen));

    // 绘制矩形
    dbTempX = BAR_OUT_MARGIN + dbDivideLen;
    double dbTempHeight;
    double dbTopHeight;
    for (int i = 0; i < nValueCount; i++)
    {
        dbTempHeight = _valueList.at(i) / nYTickValue * dbYTickLen;
        if (_useAnimation)
        {
            if (nBottomHeight - dbTempHeight > this->height() - _height) // Y轴坐标越大，越靠下
                dbTopHeight = nBottomHeight - dbTempHeight;
            else
                dbTopHeight = this->height() - _height;
        }
        else
            dbTopHeight = this->height() - _height;

        painter->save();
        if (_useMultiColor)
        {
            QBrush brush(_defaultColorList.at(i % _defaultColorList.count()));
            painter->setBrush(brush);
        }
        // nBottomHeight - 1 是因为X轴的宽度为2
        painter->drawRect(QRectF(QPointF(dbTempX, nBottomHeight - 1),
                                 QPointF(dbTempX + 4 * dbDivideLen, dbTopHeight)));
        painter->restore();

        // 绘制X轴文字
        painter->save();
        pen.setColor(_textColor);
        painter->setPen(pen);

        strText = _textList.at(i);
        textWidth = (int)fm.horizontalAdvance(strText);
        textHeight = (int)fm.height();
        painter->drawText(QPointF(dbTempX + (4 * dbDivideLen - textWidth) / 2,
                                  nBottomHeight + textHeight + 10),
                          strText);

        painter->restore();

        dbTempX += 6 * dbDivideLen;
    }
}

void JBarChart::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);

    // 设置抗拒齿，设置抗拒齿后，图形的边界更圆滑，但绘图效率会降低
    painter.setRenderHint(QPainter::Antialiasing);

    drawBar(&painter);
}

void JBarChart::resizeEvent(QResizeEvent *event)
{
    Q_UNUSED(event);
    _pAnimation->setEndValue(this->height());
}

void JBarChart::setAnimation(bool useAnimation)
{
    _useAnimation = useAnimation;
}

void JBarChart::_setValue(int height)
{
    _height = height;
    repaint();
}

int JBarChart::_getValue()
{
    return _height;
}

void JBarChart::setValue(QList<double> valueList, QStringList textList)
{
    _valueList = valueList;
    //qSort(_valueList.begin(), _valueList.end());

    if (textList.isEmpty())
    {
        for (int i = 0; i < _valueList.count(); i++)
            _textList << "";
    }
    else
        _textList = textList;

    _pAnimation->start();
}

void JBarChart::setTextColor(QColor color)
{
    _textColor = color;
}

void JBarChart::setBarColor(QColor color)
{
    _barColor = color;
}

void JBarChart::setGridColor(QColor color)
{
    _gridColor = color;
}

void JBarChart::setMultiColor(bool useMultiColor)
{
    _useMultiColor = useMultiColor;
}
